package com.weiquan.service.impl;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weiquan.common.ResultCode;
import com.weiquan.dto.LoginRequest;
import com.weiquan.dto.RegisterRequest;
import com.weiquan.entity.SysUser;
import com.weiquan.exception.BusinessException;
import com.weiquan.mapper.SysUserMapper;
import com.weiquan.service.SysUserService;
import com.weiquan.utils.JwtUtils;
import com.weiquan.utils.RedisUtils;
import com.weiquan.vo.LoginResponse;
import com.weiquan.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 * 
 * @author weiquan
 * @since 2024-01-01
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisUtils redisUtils;

    @Value("${weiquan.jwt.expire}")
    private Long jwtExpire;

    private static final String SMS_CODE_PREFIX = "sms:code:";
    private static final String SMS_LIMIT_PREFIX = "sms:limit:";
    private static final int SMS_CODE_EXPIRE = 300; // 5分钟
    private static final int SMS_LIMIT_EXPIRE = 60; // 1分钟

    @Override
    public void register(RegisterRequest request) {
        // 验证密码一致性
        if (!request.getPassword().equals(request.getConfirmPassword())) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "两次输入的密码不一致");
        }

        // 验证手机号是否已存在
        SysUser existUser = getByPhone(request.getPhone());
        if (existUser != null) {
            throw new BusinessException(ResultCode.PHONE_EXIST);
        }

        // 验证短信验证码
        validateSmsCode(request.getPhone(), request.getSmsCode());

        // 创建用户
        SysUser user = new SysUser();
        user.setPhone(request.getPhone());
        user.setPassword(BCrypt.hashpw(request.getPassword()));
        user.setNickname(StringUtils.hasText(request.getNickname()) ? 
                        request.getNickname() : "用户" + request.getPhone().substring(7));
        user.setProvince(request.getProvince());
        user.setCity(request.getCity());
        user.setDistrict(request.getDistrict());
        user.setStatus(1);

        save(user);

        // 删除验证码
        redisUtils.delete(SMS_CODE_PREFIX + request.getPhone());
    }

    @Override
    public LoginResponse login(LoginRequest request) {
        SysUser user = getByPhone(request.getPhone());
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        if (user.getStatus() == 0) {
            throw new BusinessException(ResultCode.USER_DISABLED);
        }

        // 根据登录类型验证
        if (request.getLoginType() == 1) {
            // 密码登录
            if (!StringUtils.hasText(request.getPassword())) {
                throw new BusinessException(ResultCode.PARAM_ERROR, "密码不能为空");
            }
            if (!BCrypt.checkpw(request.getPassword(), user.getPassword())) {
                throw new BusinessException(ResultCode.LOGIN_ERROR);
            }
        } else if (request.getLoginType() == 2) {
            // 验证码登录
            if (!StringUtils.hasText(request.getSmsCode())) {
                throw new BusinessException(ResultCode.PARAM_ERROR, "验证码不能为空");
            }
            validateSmsCode(request.getPhone(), request.getSmsCode());
            // 删除验证码
            redisUtils.delete(SMS_CODE_PREFIX + request.getPhone());
        } else {
            throw new BusinessException(ResultCode.PARAM_ERROR, "登录类型不正确");
        }

        // 生成token
        String token = jwtUtils.generateToken(user.getId());

        // 构建用户信息
        UserInfoVO userInfo = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfo);
        userInfo.setPhone(DesensitizedUtil.mobilePhone(user.getPhone()));

        return new LoginResponse(token, jwtExpire, userInfo);
    }

    @Override
    public void sendSmsCode(String phone) {
        // 检查发送频率限制
        String limitKey = SMS_LIMIT_PREFIX + phone;
        if (redisUtils.hasKey(limitKey)) {
            throw new BusinessException(ResultCode.SMS_SEND_FREQUENT);
        }

        // 生成6位验证码
        String code = String.format("%06d", new Random().nextInt(999999));

        // 存储验证码
        String codeKey = SMS_CODE_PREFIX + phone;
        redisUtils.set(codeKey, code, SMS_CODE_EXPIRE, TimeUnit.SECONDS);

        // 设置发送频率限制
        redisUtils.set(limitKey, "1", SMS_LIMIT_EXPIRE, TimeUnit.SECONDS);

        // TODO: 调用短信服务发送验证码
        log.info("发送短信验证码到手机号: {}, 验证码: {}", phone, code);
    }

    @Override
    public SysUser getByPhone(String phone) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getPhone, phone);
        return getOne(wrapper);
    }

    @Override
    public UserInfoVO getUserInfo(Long userId) {
        SysUser user = getById(userId);
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        UserInfoVO userInfo = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfo);
        userInfo.setPhone(DesensitizedUtil.mobilePhone(user.getPhone()));
        return userInfo;
    }

    @Override
    public void updateUserInfo(Long userId, UserInfoVO userInfo) {
        SysUser user = getById(userId);
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        user.setNickname(userInfo.getNickname());
        user.setAvatar(userInfo.getAvatar());
        user.setGender(userInfo.getGender());
        user.setBirthday(userInfo.getBirthday());
        user.setProvince(userInfo.getProvince());
        user.setCity(userInfo.getCity());
        user.setDistrict(userInfo.getDistrict());

        updateById(user);
    }

    @Override
    public void updateLoginInfo(Long userId, String loginIp) {
        SysUser user = new SysUser();
        user.setId(userId);
        user.setLoginTime(LocalDateTime.now());
        user.setLoginIp(loginIp);
        updateById(user);
    }

    /**
     * 验证短信验证码
     */
    private void validateSmsCode(String phone, String code) {
        String cacheCode = redisUtils.getString(SMS_CODE_PREFIX + phone);
        if (!StringUtils.hasText(cacheCode)) {
            throw new BusinessException(ResultCode.SMS_CODE_EXPIRED);
        }
        if (!code.equals(cacheCode)) {
            throw new BusinessException(ResultCode.SMS_CODE_ERROR);
        }
    }
}